package com.mybatis.criteria;

import basic.framework.components.mybatis.exception.SqlException;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.mybatis.criteria.SqlBuilder.ConjunctionType.OR;
import static com.mybatis.criteria.SqlBuilder.ConjunctionType.containConjunction;

public class SqlBuilder implements Serializable {
    private static final String WHERE_STR = "where";
    private static final String AND_STR = " and ";

    private SQLStatement sql = new SQLStatement();

    private SqlBuilder getSelf()
    {
        return this;
    }

    private SQLStatement sql()
    {
        return sql;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sql().sql(sb);
        return sb.toString();
    }

    public SqlBuilder where(String conditions)
    {
        sql().where.add(conditions);
        sql().lastList = sql().where;
        return getSelf();
    }

    public SqlBuilder or() {
        sql().lastList.add(OR.desc);
        return getSelf();
    }

    public SqlBuilder where(String... conditions) {
        sql().where.addAll(Arrays.asList(conditions));
        sql().lastList = sql().where;
        return getSelf();
    }

    enum ConjunctionType {
        /**
         * AND
         */
        AND(AND_STR, true),
        /**
         * OR
         */
        OR(" OR ", true),
        /**
         * IS_NOT_NULL
         */
        IS_NOT_NULL(" IS NOT NULL ", true),
        /**
         * IS_NULL
         */
        IS_NULL(" IS NULL", true),
        /**
         * AND_NEW
         */
        AND_NEW(") \nAND (", true),
        /**
         * OR_NEW
         */
        OR_NEW(") \nOR (", true);
        /**
         * 描述
         */
        private final String desc;
        private final boolean isConjunction;

        ConjunctionType(final String desc, boolean isConjunction) {
            this.desc = desc;
            this.isConjunction = isConjunction;
        }

        static boolean containConjunction(String desc) {
            ConjunctionType[] conjunctionTypes = values();
            for (ConjunctionType conjunctionType : conjunctionTypes) {
                if (conjunctionType.desc.equals(desc) && conjunctionType.isConjunction) {
                    return true;
                }
            }
            return false;

        }
    }

    private static class SafeAppendable {
        private final Appendable a;
        private boolean empty = true;

        SafeAppendable(Appendable a) {
            super();
            this.a = a;
        }

        SafeAppendable append(CharSequence s) {
            try {
                if (empty && s.length() > 0) {
                    empty = false;
                }
                a.append(s);
            } catch (IOException e) {
                throw new SqlException(e);
            }
            return this;
        }
        boolean isEmpty() {
            return empty;
        }
    }

    private static class SQLStatement implements Serializable {
        private List<String> where = new ArrayList<>();
        private List<String> lastList = new ArrayList<>();
        public String sql(Appendable appendable)
        {
            SafeAppendable safeAppendable = new SafeAppendable(appendable);
            String answer = selectSql(safeAppendable);
            return answer;
        }

        private String selectSql(SafeAppendable safeAppendable) {
            sqlClause(safeAppendable, "", where, "(", ")", AND_STR);
            return safeAppendable.toString();
        }

        private void sqlClause(SafeAppendable builder, String keyword,
                               List<String> where, String open, String close, String conjunction) {
            if (!where.isEmpty()) {
                if (!builder.isEmpty()) {
                    builder.append("\n");
                }
                builder.append(keyword);
                builder.append(" ");
                builder.append(open);
                String last = "________";
                for (int i = 0, n = where.size(); i < n; i++) {
                    String part = where.get(i);
                    if (i > 0  && !containConjunction(part) && !containConjunction(last)){//判断条件这边加and也要考虑到集合中是不是不是and or 等
                        builder.append(conjunction);
                    }
                    builder.append(part);
                    last = part;
                }
                builder.append(close);
            }
        }
    }
}
